import { defineComponent, reactive, ref, onMounted, onUnmounted } from 'vue'
import BScroll from '@better-scroll/core'
import { BScrollConstructor } from '@better-scroll/core/dist/types/BScroll'
import PullDown from '@better-scroll/pull-down'
import Pullup from '@better-scroll/pull-up'

import useDebounce from '@/hooks/useDebounce'
import { sleep, limit, toFixed } from '@/utils'
import styles from './styles.module.less'

BScroll.use(PullDown).use(Pullup)

// 這個值是指,下拉到多少像素時,鬆開就會觸發刷新
// 這個值要比狀態按鈕的高度要大
const SCROLL_STOP = 40

export default defineComponent({
  name: 'Scroll',
  props: {
    // 觸發刷新時的回調函數
    // 必須是一個異步函數
    // 函數 resolve 之後,就會使滾動區回彈
    onPullingDown: { type: Function },

    // 滾動到底部時的回調函數
    onReachBottom: { type: Function },

    // 距離底部多少像素時,觸發滾動到底部
    reachBottomDistance: { type: Number, default: 50 },

    // 必須指定高度,否則無法觸發滾動事件
    // 記得帶上單位
    height: { type: String, default: '100px' },

    // 初始化時,是否自動觸發下拉刷新
    pullDownOnInit: { type: Boolean, default: true },
  },

  setup(props, { slots }) {
    const state = reactive({
      beforePullDown: true,
      isPullingDown: false,
      isPullUpLoad: false,
      statusY: 0,
      statusScale: 0,
      statusRotate: 0,
    })
    const elRef = ref<HTMLElement | null>(null)
    let scroll: BScrollConstructor | null = null

    onMounted(() => {
      if (!elRef.value) return

      scroll = new BScroll(elRef.value, {
        pullDownRefresh: { threshold: 2 * SCROLL_STOP, stop: SCROLL_STOP },
        pullUpLoad: { threshold: props.reachBottomDistance },
      })
      scroll.on('pullingDown', handlePullingDown)
      scroll.on('pullingUp', handlePullingUp)
      scroll.on('scroll', handleScroll)

      props.pullDownOnInit && scroll.autoPullDownRefresh()
    })

    onUnmounted(() => {
      scroll?.destroy()
    })

    const handlePullingDown = useDebounce(async () => {
      state.beforePullDown = false
      state.isPullingDown = true

      await props?.onPullingDown?.()

      state.isPullingDown = false
      await sleep(500)
      scroll?.finishPullDown()
      state.beforePullDown = true
      scroll?.refresh()
    })

    const handlePullingUp = useDebounce(async () => {
      if (state.isPullUpLoad) return
      state.isPullUpLoad = true

      await props?.onReachBottom?.()

      state.isPullUpLoad = false
      await sleep(500)
      scroll?.finishPullUp()
      scroll?.refresh()
    })

    const handleScroll = (pos: { x: number; y: number }) => {
      // 更新狀態按鈕的樣式
      state.statusY = limit(toFixed(pos.y), 0)
      state.statusScale = limit(toFixed(pos.y / SCROLL_STOP), 0, 1)
      state.statusRotate = 180 * limit(toFixed(pos.y / SCROLL_STOP), 0)
    }

    return () => (
      <>
        <section class={styles.status_container}>
          <div class={styles.status_wrapper} style={{ height: `${state.statusY}px` }}>
            <span class={styles.status}>
              {state.beforePullDown ? (
                <i
                  class={styles.icon}
                  style={{ transform: `scale(${state.statusScale}) rotate(${state.statusRotate}deg)` }}
                />
              ) : (
                <i class={state.isPullingDown ? styles.icon_rolling : styles.icon} />
              )}
            </span>
          </div>
        </section>

        <section ref={elRef} style={{ height: props.height, overflow: 'hidden' }}>
          <div>
            {slots?.default?.()}
            <div class={styles.footer_status}>{state.isPullUpLoad && <i class={styles.icon_rolling} />}</div>
          </div>

          {/* 這個元素是必須的,否則無法下拉,目前不知道是什麼原因 */}
          <div style={{ height: '1px' }}></div>
        </section>
      </>
    )
  },
})
